<!DOCTYPE HTML>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />
    <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1" /> 
    <title> - 天地维杰网</title>
    <meta name="keywords" content="系统架构,shutdown,不与天斗,Domino,博客,程序员,架构师,笔记,技术,分享,java,Redis">
    
    <meta property="og:title" content="">
    <meta property="og:site_name" content="天地维杰网">
    <meta property="og:image" content="/img/author.jpg"> 
    <meta name="title" content=" - 天地维杰网" />
    <meta name="description" content="天地维杰网 | 博客 | 软件 | 架构 | Java "> 
    <link rel="shortcut icon" href="http://www.shutdown.cn/img/favicon.ico" />
    <link rel="apple-touch-icon" href="http://www.shutdown.cn/img/apple-touch-icon.png" />
    <link rel="apple-touch-icon-precomposed" href="http://www.shutdown.cn/img/apple-touch-icon.png" />
    <link href="http://www.shutdown.cn/js/vendor/font-awesome/css/font-awesome.min.css?v=4.6.2" rel="stylesheet" type="text/css" />
    <link href="http://www.shutdown.cn/js/vendor/fancybox/jquery.fancybox.css?v=2.1.5" rel="stylesheet" type="text/css" />
    <link href="http://www.shutdown.cn/css/main.css" rel="stylesheet" type="text/css" />
    <link href="http://www.shutdown.cn/css/syntax.css" rel="stylesheet" type="text/css" />
    <script type="text/javascript" id="hexo.configuration">
  var NexT = window.NexT || {};
  var CONFIG = {
    scheme: 'Pisces',
    sidebar: {"position":"left","display":"post"},
     fancybox: true, 
    motion: true
  };
</script>
</head>
<body itemscope itemtype="http://schema.org/WebPage" lang="zh-Hans">
<div class="container one-collumn sidebar-position-left page-home  ">
    <div class="headband"></div>

    <header id="header" class="header" itemscope itemtype="http://schema.org/WPHeader">
      <div class="header-inner"> <div class="site-meta  custom-logo ">

  <div class="custom-logo-site-title">
    <a href="http://www.shutdown.cn"  class="brand" rel="start">
      <span class="logo-line-before"><i></i></span>
      <span class="site-title">天地维杰网</span>
      <span class="logo-line-after"><i></i></span>
    </a>
  </div>
  <p class="site-subtitle">人如秋鸿来有信，事若春梦了无痕</p>
</div>

<div class="site-nav-toggle">
  <button>
    <span class="btn-bar"></span>
    <span class="btn-bar"></span>
    <span class="btn-bar"></span>
  </button>
</div>

<nav class="site-nav">
    <ul id="menu" class="menu">
      
      
        <li class="menu-item ">
          <a href="http://www.shutdown.cn/" rel="section">
              <i class="menu-item-icon fa fa-fw fa-home"></i> <br />首页
          </a>
        </li>
      
        <li class="menu-item ">
          <a href="http://www.shutdown.cn/categories/redis/" rel="section">
              <i class="menu-item-icon fa fa-fw fa-battery-full"></i> <br />Redis
          </a>
        </li>
      
        <li class="menu-item ">
          <a href="http://www.shutdown.cn/categories/java/" rel="section">
              <i class="menu-item-icon fa fa-fw fa-coffee"></i> <br />java
          </a>
        </li>
      
        <li class="menu-item ">
          <a href="http://www.shutdown.cn/categories/linux/" rel="section">
              <i class="menu-item-icon fa fa-fw fa-linux"></i> <br />linux
          </a>
        </li>
      
        <li class="menu-item ">
          <a href="http://www.shutdown.cn/categories/daily/" rel="section">
              <i class="menu-item-icon fa fa-fw fa-bug"></i> <br />日常问题
          </a>
        </li>
      
        <li class="menu-item ">
          <a href="http://www.shutdown.cn/categories/spring/" rel="section">
              <i class="menu-item-icon fa fa-fw fa-child"></i> <br />Spring和Springboot
          </a>
        </li>
      
        <li class="menu-item ">
          <a href="http://www.shutdown.cn/categories/spring/" rel="section">
              <i class="menu-item-icon fa fa-fw fa-fire"></i> <br />Mac相关
          </a>
        </li>
      
        <li class="menu-item ">
          <a href="http://www.shutdown.cn/categories/middleware/" rel="section">
              <i class="menu-item-icon fa fa-fw fa-gavel"></i> <br />中间件
          </a>
        </li>
      
        <li class="menu-item ">
          <a href="http://www.shutdown.cn/categories/jiagou/" rel="section">
              <i class="menu-item-icon fa fa-fw fa-rocket"></i> <br />架构
          </a>
        </li>
      
        <li class="menu-item ">
          <a href="http://www.shutdown.cn/categories/python/" rel="section">
              <i class="menu-item-icon fa fa-fw fa-ship"></i> <br />python
          </a>
        </li>
      
        <li class="menu-item ">
          <a href="http://www.shutdown.cn/categories/front/" rel="section">
              <i class="menu-item-icon fa fa-fw fa-bolt"></i> <br />前端
          </a>
        </li>
      
        <li class="menu-item ">
          <a href="http://www.shutdown.cn/categories/jvm/" rel="section">
              <i class="menu-item-icon fa fa-fw fa-balance-scale"></i> <br />JVM
          </a>
        </li>
      
        <li class="menu-item ">
          <a href="http://www.shutdown.cn/post/" rel="section">
              <i class="menu-item-icon fa fa-fw fa-archive"></i> <br />归档
          </a>
        </li>
      
        <li class="menu-item ">
          <a href="http://www.shutdown.cn/about/" rel="section">
              <i class="menu-item-icon fa fa-fw fa-user"></i> <br />关于
          </a>
        </li>
      
      <li class="menu-item menu-item-search">
        <a href="javascript:;" class="popup-trigger"> <i class="menu-item-icon fa fa-search fa-fw"></i> <br /> 搜索</a>
      </li>
    </ul>
    <div class="site-search">
      <div class="popup">
 <span class="search-icon fa fa-search"></span>
 <input type="text" id="local-search-input">
 <div id="local-search-result"></div>
 <span class="popup-btn-close">close</span>
</div>

    </div>
</nav>

 </div>
    </header>

    <main id="main" class="main">
      <div class="main-inner">
        <div class="content-wrap">
          <div id="content" class="content">
            
<section id="posts" class="posts-expand">
  <article class="post post-type-normal " itemscope itemtype="http://schema.org/Article">
    <header class="post-header">
      <h1 class="post-title" itemprop="name headline">
        <a class="post-title-link" href="http://www.shutdown.cn/post/bigkey%E5%88%A0%E9%99%A4%E9%97%AE%E9%A2%98/" itemprop="url">
        
        </a>
      </h1>
      <div class="post-meta">
      <span class="post-time">
<span class="post-meta-item-icon">
    <i class="fa fa-calendar-o"></i>
</span>
<span class="post-meta-item-text">时间：</span>
<time itemprop="dateCreated" datetime="2016-03-22T13:04:35+08:00" content="0001-01-01">
    0001-01-01
</time>
</span> 
      
      
       <span>
&nbsp; | &nbsp;
<span class="post-meta-item-icon">
    <i class="fa fa-eye"></i>
</span>
<span class="post-meta-item-text">阅读：</span>
<span class="leancloud-visitors-count">677 字 ~4分钟</span>
</span>
      </div>
    </header>
    <div class="post-body" itemprop="articleBody">
    

    

<h2 id="一-慢操作分析">一、慢操作分析</h2>

<p>redis 的慢操作已经有了，如果没有，我们可以自己去 redis 服务器查看历史的慢日志操作，或者有对应的慢操作监控系统也可以发现问题，这里不做展开。</p>

<p>接下来我们就要看一看为什么这么慢。</p>

<p>看了下项目中的实现代码，结合日志一分析，发现是一个 redis bigkey。</p>

<p>一个 redis key，对应的是一个 map, 里面防了几十万的 key/value。删除的时候一把直接删除，自然是慢的。</p>

<p>本文带大家一起分析下 redis bigkey 删除的解决方案，希望你工作中遇到类似问题提供一个解决思路。</p>

<h2 id="二-处理-bigkey">二、处理 bigkey</h2>

<p>bigkey是指key对应的value所占的内存空间比较大。</p>

<p>例如一个字符串类 型的value可以最大存到512MB，一个列表类型的value最多可以存储2^32-1个元素。</p>

<p>如果按照数据结构来细分的话，一般分为字符串类型bigkey和非字符串类型bigkey。</p>

<h2 id="三-危害">三、危害</h2>

<p>bigkey的危害体现在三个方面：</p>

<h3 id="1-内存空间不均匀-平衡">1.内存空间不均匀（平衡）:</h3>

<p>例如在Redis Cluster中，bigkey会造成节点的内存空间使用不均匀。</p>

<h3 id="2-超时阻塞">2.超时阻塞：</h3>

<p>由于Redis单线程的特性，操作bigkey比较耗时，也就意味着阻塞Redis可能性增大。</p>

<h3 id="3-网络拥塞">3.网络拥塞：</h3>

<p>每次获取bigkey产生的网络流量较大，假设一个bigkey为1MB，每秒访问量为1000，那么每秒产生1000MB的流量，对于普通的千兆网卡（按照字节算是128MB/s）的服务器来说简直是灭顶之灾，而且一般服务器会采用单机多实例的方式来部署，也就是说一个bigkey可能会对其他实例造成影响，其后果不堪设想。图12-3演示了网络带宽bigkey占用的瞬间。</p>

<h2 id="四-如何发现">四、如何发现</h2>

<p><code>redis-cli --bigkeys</code> 可以命令统计bigkey的分布。</p>

<p>但是在生产环境中，开发和运维人员更希望自己可以定义bigkey的大小，而且更希望找到真正的bigkey都有哪些，这样才可以去定位、解决、优化问题。</p>

<p>判断一个key是否为bigkey，只需要执行 <code>debug object key</code> 查看serializedlength属性即可，它表示key对应的value序列化之后的字节数，</p>

<p>例如我们执行如下操作：</p>
<div class="highlight"><pre style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-text" data-lang="text">127.0.0.1:6379&gt; debug object key
Value at:0x7fc06c1b1430 refcount:1 encoding:raw serializedlength:1256350 lru:11686193
lru_seconds_idle:20</code></pre></div>
<p>可以发现serializedlength=11686193字节，约为1M，同时可以看到encoding是raw，也就是字符串类型。</p>

<p>那么可以通过strlen来看一下字符串的字节数为2247394字节，约为2MB：</p>
<div class="highlight"><pre style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-text" data-lang="text">127.0.0.1:6379&gt; strlen key
(integer) 2247394</code></pre></div>
<p>serializedlength不代表真实的字节大小，它返回对象使用RDB编码序列化后的长度，值会偏小，但是对于排查bigkey有一定辅助作用，因为不是每种数据结构都有类似strlen这样的方法。</p>

<h2 id="五-实际生产的操作方式">五、实际生产的操作方式</h2>

<p>在实际生产环境中发现bigkey的两种方式如下：</p>

<h3 id="被动收集">被动收集：</h3>

<p>许多开发人员确实可能对bigkey不了解或重视程度不够，但是这种bigkey一旦大量访问，很可能就会带来命令慢查询和网卡跑满问题，开发人员通过对异常的分析通常能找到异常原因可能是bigkey，这种方式虽然不是被笔者推荐的，但是在实际生产环境中却大量存在，建议修改Redis客户端，当抛出异常时打印出所操作的key，方便排bigkey问题。</p>

<h3 id="主动检测">主动检测：</h3>

<p>scan+debug object：如果怀疑存在bigkey，可以使用scan命令渐进的扫描出所有的key，分别计算每个key的serializedlength，找到对应bigkey进行相应的处理和报警，这种方式是比较推荐的方式。</p>

<h2 id="六-如何删除">六、如何删除</h2>

<p>因为 redis 是单线程的，删除比较大的 keys 就会阻塞其他的请求。</p>

<p>当发现Redis中有bigkey并且确认要删除时，如何优雅地删除bigkey？</p>

<p>无论是什么数据结构，del命令都将其删除。</p>

<p>但是相信通过上面的分析后你一定不会这么做，因为删除bigkey通常来说会阻塞Redis服务。</p>

<p>下面给出一组测试数据分别对string、hash、list、set、sorted set五种数据结构的bigkey进行删除，bigkey的元素个数和每个元素的大小不尽相同。</p>

<h2 id="七-删除时间测试">七、删除时间测试</h2>

<p>下面测试和服务器硬件、Redis版本比较相关，可能在不同的服务器上执行速度不太相同，但是能提供一定的参考价值</p>

<h3 id="1-字符串类删除测试">1.字符串类删除测试</h3>

<p>表12-3展示了删除512KB~10MB的字符串类型数据所花费的时间，总体来说由于字符串类型结构相对简单，删除速度比较快，但是随着value值的不断增大，删除速度也逐渐变慢。</p>

<div  align="center"><img src="https://wejack639.oss-cn-beijing.aliyuncs.com/blogimages/img/20220104195406.png" width = 900 /> 表12-3</div>

<h3 id="2-非字符串类删除测试">2.非字符串类删除测试</h3>

<p>表12-4展示了非字符串类型的数据结构在不同数量级、不同元素大小下对bigkey执行del命令的时间，总体上看元素个数越多、元素越大，删除时间越长，相对于字符串类型，这种删除速度已经足够可以阻塞Redis。</p>

<div  align="center"><img src="https://wejack639.oss-cn-beijing.aliyuncs.com/blogimages/img/20220104195424.png" width = 900 />表12-4 删除hash、list、set、sorted set四种数据结构不同数量不同元素大小的耗时</div>

<p>从上分析可见，除了string类型，其他四种数据结构删除的速度有可能很慢，这样增大了阻塞Redis的可能性。</p>

<h2 id="八-如何提升删除的效率">八、如何提升删除的效率</h2>

<p>既然不能用del命令，那有没有比较优雅的方式进行删除呢，这时候就需要将第2章介绍的scan命令的若干类似命令拿出来：sscan、hscan、zscan。</p>

<h3 id="1-string">1.string</h3>

<p>字符串删除一般不会造成阻塞</p>
<div class="highlight"><pre style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-text" data-lang="text">del bigkey</code></pre></div>
<h3 id="2-hash-list-set-sorted-set">2.hash、list、set、sorted set</h3>

<p>下面以hash为例子，使用hscan命令，每次获取部分（例如100个）fieldvalue，再利用hdel删除每个field（为了快速可以使用Pipeline）：</p>
<div class="highlight"><pre style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-text" data-lang="text">public void delBigHash(String bigKey) {
    Jedis jedis = new Jedis(“127.0.0.1”, 6379);
    // 游标
    String cursor = “0”;
    while (true) {
        ScanResult&lt;Map.Entry&lt;String, String&gt;&gt; scanResult = jedis.hscan(bigKey, cursor, new ScanParams().count(100));
        // 每次扫描后获取新的游标
        cursor = scanResult.getStringCursor();
        // 获取扫描结果
        List&lt;Entry&lt;String, String&gt;&gt; list = scanResult.getResult();
        if (list == null || list.size() == 0) {
            continue;
        }
        String[] fields = getFieldsFrom(list);
        // 删除多个field
        jedis.hdel(bigKey, fields);
        // 游标为0时停止
        if (cursor.equals(“0”)) {
            break;
        }
    }
    // 最终删除key
    jedis.del(bigKey);
}

/**
* 获取field数组
* @param list
* @return
*/
private String[] getFieldsFrom(List&lt;Entry&lt;String, String&gt;&gt; list) {
    List&lt;String&gt; fields = new ArrayList&lt;String&gt;();
    for(Entry&lt;String, String&gt; entry : list) {
        fields.add(entry.getKey());
    }
    return fields.toArray(new String[fields.size()]);
}</code></pre></div>
<p>请勿忘记每次执行到最后执行del key操作。</p>

<h2 id="九-最佳实践思路">九、最佳实践思路</h2>

<p>由于开发人员对Redis的理解程度不同，在实际开发中出现bigkey在所难免，重要的是，能通过合理的检测机制及时找到它们，进行处理。</p>

<p>作为开发人员在业务开发时应注意不能将Redis简单暴力的使用，应该在数据结构的选择和设计上更加合理，例如出现了bigkey，</p>

<p>（1）要思考一下可不可以做一些优化（例如拆分数据结构）尽量让这些bigkey消失在业务中，</p>

<p>（2）如果bigkey不可避免，也要思考一下要不要每次把所有元素都取出来（例如有时候仅仅需要hmget，而不是hgetall）。</p>

<p>（3）最后，可喜的是，Redis将在4.0版本支持lazy delete free的模式，那时删除bigkey不会阻塞Redis。</p>

<h2 id="十-如何优雅的删除">十、如何优雅的删除</h2>

<h3 id="1-重构">1.重构</h3>

<p>重新构建自己的业务 key。</p>

<p>让 key/value 更加小，使用纯字符串。</p>

<ul>
<li>缺点</li>
</ul>

<p>有时候难以对旧的代码进行兼容，调整难度较大。</p>

<h3 id="2-使用-lazy-free">2.使用 lazy free</h3>

<p>这个是 redis 4.0 以后的特性。</p>

<p>可能会受限于版本，导致无法使用。</p>

<ul>
<li>查看版本</li>
</ul>

<p>redis 压缩包有文件 <code>cat 00-RELEASENOTES</code> 可以查看对应的版本信息。</p>
<div class="highlight"><pre style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-text" data-lang="text">Redis 2.8 release notes
=======================

** IMPORTANT ** Check the &#39;Migrating from 2.6 to 2.8&#39; section at the end of
                this file for information about what changed between 2.6 and
                2.8 and how this may affect your application.

--------------------------------------------------------------------------------
Upgrade urgency levels:

LOW:      No need to upgrade unless there are new features you want to use.
MODERATE: Program an upgrade of the server, but it&#39;s not urgent.
HIGH:     There is a critical bug that may affect a subset of users. Upgrade!
CRITICAL: There is a critical bug affecting MOST USERS. Upgrade ASAP.
--------------------------------------------------------------------------------

--[ Redis 2.8.6 ] Release date: 13 Feb 2014</code></pre></div>
<p>可知当前版本为：2.8.6</p>

<h3 id="3-使用-expire-设置过期">3.使用 expire 设置过期</h3>

<p>需要熟知 redis 的淘汰策略。</p>

<p>（1）惰性淘汰</p>

<p>（2）定时删除</p>

<p>（3）定期删除</p>

<p>其中定期删除，是一个异步的进程去处理的，不会阻塞主进程。</p>

<p>其中设置超时时间，是为了限制每一次的操作时间，从而更好的清空数据，释放内存。</p>

<ul>
<li>缺点</li>
</ul>

<p>需要知道具体的淘汰策略</p>

<p>对内存是不够友好的</p>

<p>可能要根据业务进行调整，比如本来显式删除，可以放在凌晨。</p>

<p>如果使用定期删除，被淘汰的时间就变得不固定了。</p>

<h2 id="十一-实战代码">十一、实战代码</h2>

<h3 id="1-jediscluster示例">1.JedisCluster示例</h3>
<div class="highlight"><pre style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-text" data-lang="text">/**
 * 刪除 BIG key
 * 应用场景：对于 big key，可以使用 hscan 首先分批次删除，最后统一删除
 * （1）比直接删除的耗时变长，但是不会产生慢操作。
 * （2）新业务实现尽可能拆开，不要依赖此方法。
 * @param key key
 * @param scanCount 单次扫描总数（建议值：100）
 * @param intervalMills 分批次的等待时间（建议值：5）
 */
void removeBigKey(final String key, final int scanCount, final long intervalMills)</code></pre></div>
<p>实现</p>
<div class="highlight"><pre style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-text" data-lang="text">JedisCluster jedisCluster = redisClusterTemplate.getJedisClusterInstance();
// 游标初始值为0
String cursor = ScanParams.SCAN_POINTER_START;
ScanParams scanParams = new ScanParams();
scanParams.count(scanCount);
while (true) {
 // 每次扫描后获取新的游标
 ScanResult&lt;Map.Entry&lt;String, String&gt;&gt; scanResult = jedisCluster.hscan(key, cursor, scanParams);
 cursor = scanResult.getStringCursor();
 // 获取扫描结果为空
 List&lt;Map.Entry&lt;String, String&gt;&gt; list = scanResult.getResult();
 if (CollectionUtils.isEmpty(list)) {
  break;
 }
 // 构建多个删除的 key
 String[] fields = getFieldsKeyArray(list);
 jedisCluster.hdel(key, fields);
 // 游标为0时停止
 if (ScanParams.SCAN_POINTER_START.equals(cursor)) {
  break;
 }
 // 沉睡等待，避免对 redis 压力太大
 DateUtil.sleepInterval(intervalMills, TimeUnit.MILLISECONDS);
}
// 执行 key 本身的删除
jedisCluster.del(key);</code></pre></div>
<ul>
<li><p>构建的 key</p>
<div class="highlight"><pre style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-text" data-lang="text">/**
* 获取对应的 keys 信息
* @param list 列表
* @return 结果
*/
private String[] getFieldsKeyArray(List&lt;Map.Entry&lt;String, String&gt;&gt; list) {
String[] strings = new String[list.size()];
for(int i = 0; i &lt; list.size(); i++) {
strings[i] = list.get(i).getKey();
}
return strings;
}</code></pre></div></li>
</ul>

<h3 id="redistemplate-的写法">redisTemplate 的写法</h3>

<p><strong>语法</strong></p>

<p>估计是 redis 进行了一次封装，发现还是存在很多坑。</p>

<p>语法如下：</p>
<div class="highlight"><pre style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-text" data-lang="text">/**
 * 获取集合的游标。通过游标可以遍历整个集合。
 * ScanOptions 这个类中使用了构造者 工厂方法 单例。 通过它可以配置返回的元素
 * 个数 count  与正则匹配元素 match. 不过count设置后不代表一定返回的就是count个。这个只是参考
 * 意义
 *
 * @param key
 * @param options 
 * @return
 * @since 1.4
 */
Cursor&lt;V&gt; scan(K key, ScanOptions options);</code></pre></div>
<p><strong>注意的坑</strong></p>

<p>实际上这个方法存在很多需要注意的坑：</p>

<p>（1）cursor 要关闭，否则会内存泄漏</p>

<p>（2）cursor 不要重复关闭，或者会报错</p>

<p>（3）cursor 经测试，直接指定的 count 设置后，返回的结果其实是全部，所以需要自己额外处理</p>

<p><strong>参考代码如下：</strong></p>

<p>声明</p>
<div class="highlight"><pre style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-text" data-lang="text">@Autowired
private StringRedisTemplate template;</code></pre></div>
<p>核心代码</p>
<div class="highlight"><pre style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-text" data-lang="text">public void removeBigKey(String key, int scanCount, long intervalMills) throws CacheException {
    final ScanOptions scanOptions = ScanOptions.scanOptions().count(scanCount).build();
    //TRW 避免内存泄漏
 try(Cursor&lt;Map.Entry&lt;Object,Object&gt;&gt; cursor =
                template.opsForHash().scan(key, scanOptions)) {
     if(ObjectUtil.isNotNull(cursor)) {
            // 执行循环删除
            List&lt;String&gt; fieldKeyList = new ArrayList&lt;&gt;();
            while (cursor.hasNext()) {
                String fieldKey = String.valueOf(cursor.next().getKey());
                fieldKeyList.add(fieldKey);
                if(fieldKeyList.size() &gt;= scanCount) {
                    // 批量删除
                    Object[] fields = fieldKeyList.toArray();
                    template.opsForHash().delete(key, fields);
                    logger.info(&#34;[Big key] remove key: {}, fields size: {}&#34;,
                            key, fields.length);
                    // 清空列表，重置操作
                    fieldKeyList.clear();
                    // 沉睡等待，避免对 redis 压力太大
                    DateUtil.sleepInterval(intervalMills, TimeUnit.MILLISECONDS);
                }
            }
        }
        // 最后 fieldKeyList 中可能还有剩余，不过一般数量不大，直接删除速度不会很慢
  // 执行 key 本身的删除
  this.opsForValueDelete(key);
 } catch (Exception e) {
  // log.error();
 }
}</code></pre></div>
<p>这里我们使用 TRW 保证 cursor 被关闭，自己实现 scanCount 一次进行删除，避免 1 个 1 个删除网络交互较多。</p>

<p>使用睡眠保证对 Redis 压力不要过大。</p>

    </div>
    <footer class="post-footer">
     

     <div class="post-nav">
    <div class="post-nav-next post-nav-item">
    
        <a href="http://www.shutdown.cn/post/bigkey/" rel="next" title="">
        <i class="fa fa-chevron-left"></i> 
        </a>
    
    </div>

    <div class="post-nav-prev post-nav-item">
    
        <a href="http://www.shutdown.cn/post/cachecloud%E5%AE%89%E8%A3%85%E9%83%A8%E7%BD%B2/" rel="prev" title="">
         <i class="fa fa-chevron-right"></i>
        </a>
    
    </div>
</div>
      
     
     
     






    </footer>
  </article>
</section>

          </div>
        </div>
        <div class="sidebar-toggle">
  <div class="sidebar-toggle-line-wrap">
    <span class="sidebar-toggle-line sidebar-toggle-line-first"></span>
    <span class="sidebar-toggle-line sidebar-toggle-line-middle"></span>
    <span class="sidebar-toggle-line sidebar-toggle-line-last"></span>
  </div>
</div>
<aside id="sidebar" class="sidebar">
  <div class="sidebar-inner">

    <section class="site-overview sidebar-panel  sidebar-panel-active ">
      <div class="site-author motion-element" itemprop="author" itemscope itemtype="http://schema.org/Person">
    <img class="site-author-image" itemprop="image"
        src="http://www.shutdown.cn/img/author.jpg"
        alt="不与天斗Domino" />
    <p class="site-author-name" itemprop="name">不与天斗Domino</p>
    <p class="site-description motion-element" itemprop="description"> 
        Programmer &amp; Architect</p>
</div>
      <nav class="site-state motion-element">
    <div class="site-state-item site-state-posts">
      <a href="http://www.shutdown.cn/post/">
        <span class="site-state-item-count">173</span>
        <span class="site-state-item-name">日志</span>
      </a>
    </div>
    <div class="site-state-item site-state-categories">    
        <a href="http://www.shutdown.cn/categories/">      
         
        <span class="site-state-item-count">10</span>
        
        <span class="site-state-item-name">分类</span>
        
        </a>
    </div>

    <div class="site-state-item site-state-tags">
        <a href="http://www.shutdown.cn/tags/">
         
        <span class="site-state-item-count">63</span>
        
        <span class="site-state-item-name">标签</span>
        </a>
    </div>
</nav>
      
      

      

      <div class="links-of-blogroll motion-element inline">
<script type="text/javascript" src="//rf.revolvermaps.com/0/0/8.js?i=&amp;m=0&amp;s=220&amp;c=ff0000&amp;cr1=ffffff&amp;f=arial&amp;l=33&amp;bv=35" async="async"></script>
</div>

    </section>
    
  </div>
</aside>

      </div>
    </main>
   
    <footer id="footer" class="footer">
      <div class="footer-inner">
        <div class="copyright" >
  <span itemprop="copyrightYear">  &copy; 
  2013 - 2022</span>
  <span class="with-love"><i class="fa fa-heart"></i></span>
  <span class="author" itemprop="copyrightHolder">天地维杰网</span>
  <span class="icp" itemprop="copyrightHolder"><a href="https://beian.miit.gov.cn/" target="_blank">京ICP备13019191号-1</a></span>
</div>
<div class="powered-by">
  Powered by - <a class="theme-link" href="http://gohugo.io" target="_blank" title="hugo" >Hugo v0.63.2</a>
</div>
<div class="theme-info">
  Theme by - <a class="theme-link" href="https://github.com/xtfly/hugo-theme-next" target="_blank"> NexT
  </a>
</div>


      </div>
    </footer>

    <div class="back-to-top">
      <i class="fa fa-arrow-up"></i>
      <span id="scrollpercent"><span>0</span>%</span>
    </div>
  </div>

  

<script type="text/javascript">
  if (Object.prototype.toString.call(window.Promise) !== '[object Function]') {
    window.Promise = null;
  }
</script>
<script type="text/javascript" src="http://www.shutdown.cn/js/vendor/jquery/index.js?v=2.1.3"></script>
<script type="text/javascript" src="http://www.shutdown.cn/js/vendor/fastclick/lib/fastclick.min.js?v=1.0.6"></script> 
<script type="text/javascript" src="http://www.shutdown.cn/js/vendor/jquery_lazyload/jquery.lazyload.js?v=1.9.7"></script>
<script type="text/javascript" src="http://www.shutdown.cn/js/vendor/velocity/velocity.min.js?v=1.2.1"></script>
<script type="text/javascript" src="http://www.shutdown.cn/js/vendor/velocity/velocity.ui.min.js?v=1.2.1"></script>
<script src="http://www.shutdown.cn/js/vendor/ua-parser-js/dist/ua-parser.min.js?v=0.7.9"></script>

<script src="http://www.shutdown.cn/js/vendor/fancybox/jquery.fancybox.pack.js?v=2.1.5"></script>

<script type="text/javascript" src="http://www.shutdown.cn/js/utils.js"></script>
<script type="text/javascript" src="http://www.shutdown.cn/js/motion.js"></script>
<script type="text/javascript" src="http://www.shutdown.cn/js/affix.js"></script>
<script type="text/javascript" src="http://www.shutdown.cn/js/schemes/pisces.js"></script>

<script type="text/javascript" src="http://www.shutdown.cn/js/scrollspy.js"></script>
<script type="text/javascript" src="http://www.shutdown.cn/js/post-details.js"></script>
<script type="text/javascript" src="http://www.shutdown.cn/js/toc.js"></script>

<script type="text/javascript" src="http://www.shutdown.cn/js/bootstrap.js"></script>

<script type="text/javascript" src="http://www.shutdown.cn/js/search.js"></script>
<script type="text/x-mathjax-config">
  MathJax.Hub.Config({
    extensions: ["tex2jax.js"],
    jax: ["input/TeX", "output/HTML-CSS"],
    tex2jax: {
      inlineMath: [ ['$','$'] ],
      displayMath: [ ['$$','$$'] ],
      processEscapes: true
    },
    "HTML-CSS": { fonts: ["TeX"] }
  });
</script>
<script src='https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.5/MathJax.js?config=TeX-AMS-MML_HTMLorMML' async></script>
</body>
</html>